package com.server.core.domain;

import com.server.core.action.AbstractAction;
import com.server.core.filter.AbstractFilter;
import com.server.core.util.ActionUtil;
import com.server.core.util.FilterUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.cookie.Cookie;
import io.netty.handler.stream.ChunkedFile;
import io.netty.util.concurrent.Future;

import javax.activation.MimetypesFileTypeMap;
import java.io.*;
import java.lang.reflect.Method;

public class Response {
    private final HttpVersion httpVersion = HttpVersion.HTTP_1_1;
    private HttpHeaders header = new DefaultHttpHeaders();
    private ChannelHandlerContext ctx;
    private ByteBuf content = Unpooled.EMPTY_BUFFER;
    private String DEFAULT_CONTENT_TYPE = "text/html;charset=UTF-8";
    private HttpResponseStatus status = HttpResponseStatus.OK;
    private File file;
    private MimetypesFileTypeMap MIME = new MimetypesFileTypeMap();
    private final int CHUNK_SIZE = 8192;

    public File getFile() {
        return file;
    }

    public void setFile(String filePath) {
        File f = new File(filePath);
        if (!f.exists()) {
            ///// 处理异常
        }
        this.file = f;
    }

    public Response(ChannelHandlerContext ctx) {
        this.ctx = ctx;
    }

    public void setContentType(String vaule) {
        setHeader("Content-Type", vaule);
    }

    public void setHeader(HttpHeaders header) {
        this.header = header;
    }

    public void setHeader(String key, Object val) {
        header.set(key, val);
    }

    public void setContent(byte[] bytes) {
        this.content = Unpooled.copiedBuffer(bytes);
    }

    public HttpResponseStatus getStatus() {
        return status;
    }

    public void setStatus(HttpResponseStatus status) {
        this.status = status;
    }

    /**
     * @param ctx
     * @param request
     * @desc 底层数据发送模块
     * 1- 静态资源判断
     * 2- 执行过滤器
     * 3- 返回restful结果
     */
    public void send(ChannelHandlerContext ctx, Request request) {
        AbstractAction abstractAction = null;
        Method method = null;
        ///不处理 ico请求
        if (request.getUri().equals("/favicon.ico")) {
            return;
        }

        try {
            /// 如果是非静态资源
            if ((abstractAction = ActionUtil.getAction(
                    (method = ActionUtil.getMethod(request.getMethod().toString(), request.getUri()))
            )) != null) {
                ///  执行过滤器
                AbstractFilter filter;
                if ((filter = FilterUtil.FilterChainBuild(abstractAction.getClass())) != null) {
                    filter.doChain(request, this);
                }
                ///  执行action
                abstractAction.doAction(request, this, method, abstractAction);
            }
            /// 静态资源处理
            else {
                sendStaticResource(request);
            }
        } catch (Exception e) {
            e.printStackTrace();
            ////处理异常
            AbstractAction.doErr(e, this);
            this.sendText();
        }
    }

    public ChannelFuture sendText() {
        FullHttpResponse fullHttpResponse =
                new DefaultFullHttpResponse(httpVersion, status, content);
        ///设置content-length，应对Http1.1的pipeline
        setHeader(HttpHeaderNames.CONTENT_LENGTH.toString(), content.readableBytes());
        setContentType(DEFAULT_CONTENT_TYPE);
        fullHttpResponse.headers().set(header);
        return ctx.writeAndFlush(fullHttpResponse);
    }

    /*
     *发送静态资源
     */
    public void sendStaticResource(Request request) throws Exception {
        HttpResponse response =
                new DefaultHttpResponse(httpVersion, status);

        setFile("/" + this.getClass().getClassLoader().getResource("").getPath().substring(1) + "../../src/main/resources" + request.getUri());

        RandomAccessFile raf = new RandomAccessFile(file, "r");
        setContentType(fileToContentType());
        setHeader(HttpHeaderNames.CONTENT_LENGTH.toString(), raf.length());

        response.headers().set(header);
        ////先发送头部
        ctx.write(response);

        ////发送数据部分
        ChunkedFile chunkedFile = new ChunkedFile(raf, 0, raf.length(), CHUNK_SIZE);
        Future future = ctx.write(new ChunkedFile(raf, 0,
                raf.length(), CHUNK_SIZE), ctx.newProgressivePromise());

        Future future1 = sendEmptyLast();
    }

    /*发送文件
     * @param ctx
     */
    public void sendFile() {
        ctx.write(sendFileHeader(ctx, file));
        ctx.write(sendFileRegion(ctx, file));
        sendEmptyLast();
    }


    /*构造文件header
     * @param cookie
     */
    public FullHttpResponse sendFileHeader(ChannelHandlerContext ctx, File file) {
        long length = file.length();
        //将文件类型设置为流
        setHeader("Content-Type", "application/octet-stream");
        setHeader(HttpHeaderNames.CONTENT_LENGTH.toString(), file.length());
        FullHttpResponse fullHttpResponse = new DefaultFullHttpResponse(httpVersion, status);
        fullHttpResponse.headers().set(header);

        return fullHttpResponse;
    }

    /*构造文件体
     * @param cookie
     */
    public DefaultFileRegion sendFileRegion(ChannelHandlerContext ctx, File file) {
        try {
            final RandomAccessFile raf = new RandomAccessFile(file, "r");

            // 内容长度
            long fileLength = raf.length();
            DefaultFileRegion region = new DefaultFileRegion(raf.getChannel(), 0, fileLength);
            return region;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 发送结尾标记，表示发送结束
     *
     * @return ChannelFuture
     */
    private ChannelFuture sendEmptyLast() {
        final ChannelFuture lastContentFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);

        return lastContentFuture;
    }

    /**
     * 设置cookie
     */
    public void addCookie(Cookie cookie) {
        header.add("Set-Cookie", cookie);
    }

    private String fileToContentType() {
        String fileName = file.getName();
        if (fileName.endsWith(".html")) {
            return "text/html;charset=utf8";
        } else if (fileName.endsWith(".png")) {
            return "image/png";
        } else if (fileName.endsWith(".jpg")) {
            return "image/jpg";
        } else if (fileName.endsWith(".css")) {
            return "text/html;charset=utf8";
        } else if (fileName.endsWith(".js")) {
            return "application/javascript";
        }
        throw new NullPointerException();
    }

    /**
     * 302临时重定向
     */
    public ChannelFuture doRedirect(String uri, Response response) {
        setStatus(HttpResponseStatus.FOUND);
        setHeader(HttpHeaderNames.LOCATION.toString(), uri);
        return sendText();
    }
}
